www.gusucode.com > VC++ 多种窗体动画特效源码-源码程序 > VC++ 多种窗体动画特效源码-源码程序/code/VisualFx.cpp

    /*#############################################################################
# VISUALFX.CPP
# Download by http://www.NewXing.com
# SCA Software International S.A.
# http://www.scasoftware.com
# scaadmin@scasoftware.com
#
# Copyright (c) 1999 SCA Software International S.A.
#
# Date: 03.01.2000
# Author: Zoran M.Todorovic
#
# This software is provided "AS IS", without a warranty of any kind.
# You are free to use/modify this code but leave this header intact.
#
#############################################################################*/

#include "stdafx.h"
#include <afxpriv.h>        // Needed for WM_SIZEPARENT

#include "VisualFx.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//=============================================================================
// class TTabItem
//
//=============================================================================

// Create a tab item 
TTabItem::TTabItem(CWnd *pParent, LPCTSTR szLabel)
{
  m_pWnd = NULL;
  m_nMinX = m_nMaxX = 0;
  m_bVisible = TRUE;
  m_bEnabled = TRUE;
  m_bWndEnabled = TRUE;
  RECT rect;
  ::ZeroMemory(&rect,sizeof(RECT));
  m_pCaption = new CStatic;
  ASSERT(m_pCaption);
  m_pCaption->Create(szLabel, WS_CHILD|SS_CENTER|WS_VISIBLE,rect,pParent);
}

TTabItem::TTabItem(const TTabItem& obj)
{
  *this = obj;
}

TTabItem& TTabItem::operator=(const TTabItem& obj)
{
  m_pWnd = obj.m_pWnd;
  m_pCaption = obj.m_pCaption;
  m_bWndEnabled = obj.m_bWndEnabled;
  m_bEnabled = obj.m_bEnabled;
  m_bVisible = obj.m_bVisible;
  m_nMinX = obj.m_nMinX;
  m_nMaxX = obj.m_nMaxX;
  return *this;
}

TTabItem::~TTabItem()
{
  // This is done in TVisualFramework::Destroy()
  //if (m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
  //  delete m_pWnd;
  ASSERT(m_pCaption);
  delete m_pCaption;
}

// Set rectangle for tab caption
void TTabItem::SetRect(CRect& rect)
{
  ASSERT(m_pCaption);
  m_pCaption->MoveWindow(&rect);
}

// Set font for tab caption
void TTabItem::SetFont(CFont *pFont)
{
  ASSERT(m_pCaption);
  ASSERT(pFont);
  m_pCaption->SetFont(pFont,FALSE);
}

// Get tab caption text
CString TTabItem::GetText(void)
{
  ASSERT(m_pCaption);
  CString str;
  m_pCaption->GetWindowText(str);
  return str;
}

int TTabItem::GetLength(void)
{
  return m_nMaxX - m_nMinX;
}

// Set tab caption text
void TTabItem::SetText(LPCTSTR szLabel)
{
  ASSERT(m_pCaption);
  ASSERT(szLabel);
  m_pCaption->SetWindowText(szLabel);
}

// Enable/disable a window
void TTabItem::Enable(BOOL bEnable)
{
  m_bWndEnabled = bEnable;
}

// Enable/disable tab caption
void TTabItem::EnableTab(BOOL bEnable)
{
  ASSERT(m_pCaption);
  m_bEnabled = bEnable;
  m_pCaption->EnableWindow(m_bEnabled);
}

// Show/hide tab caption
void TTabItem::ShowTab(BOOL bShow)
{
  ASSERT(m_pCaption);
  m_bVisible = bShow;
  m_pCaption->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
}

CWnd *TTabItem::GetSafeWnd(void)
{
  return (m_pWnd && ::IsWindow(m_pWnd->m_hWnd)) ? m_pWnd : NULL;
}

//=============================================================================
// class TTabWnd
//
//=============================================================================

#define TABWND_DEFAULT_ID 0x2578
#define TABWND_HEIGHT     30    // Height of the gray border between the toolbar 
                                // and the client area
#define TAB_HEIGHT        20    // Height on the normal tab
#define TABSEL_HEIGHT     20    // Height of the selected tab
#define TAB_SPACE         6     // Add to tab caption text width
#define TAB_DEPL          4     // Distance between the tabs and the client area
#define TAB_MAXLEN        200

IMPLEMENT_DYNCREATE(TTabWnd,CWnd)

BEGIN_MESSAGE_MAP(TTabWnd, CWnd)
  //{{AFX_MSG_MAP(TTabWnd)
  ON_MESSAGE(WM_SIZEPARENT, OnSizeParent)
  ON_WM_ERASEBKGND()
  ON_WM_PAINT()
  ON_WM_LBUTTONUP()
  ON_WM_DESTROY()
  ON_WM_SIZE()
	ON_WM_CREATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

TTabWnd::TTabWnd()
{
  m_nSelectedTab = 0;
  m_bLockFlag = FALSE;
  m_nTabPos = TP_BOTTOM;
  // cache most used resources
  m_BrushBlack.CreateSolidBrush(RGB(0,0,0));
  m_BrushLGray.CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
  m_PenBlack.CreatePen(PS_SOLID, 1, (COLORREF)0);
  m_PenLGray.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNFACE));
  m_PenWhite.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNHIGHLIGHT));
  m_PenWhite2.CreatePen(PS_SOLID, 2, ::GetSysColor(COLOR_BTNHIGHLIGHT));
  m_PenDGray.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNSHADOW));
  m_PenDGray2.CreatePen(PS_SOLID, 2, ::GetSysColor(COLOR_BTNSHADOW));
}

TTabWnd::~TTabWnd()
{
}

// Find a tab within this tab window
TTabItem *TTabWnd::findTabItem(int nIndex)
{
  int nNdx = 0;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    if (nNdx == nIndex)
      return (*iterator);
    nNdx ++;
  }
  return NULL;
}

// Create a tab window
BOOL TTabWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, 
                     DWORD dwStyle, const RECT& prect, CWnd* pParentWnd, 
                     UINT nID, CCreateContext *pContext)
{
  ASSERT(pParentWnd);

  dwStyle &= ~WS_BORDER;
  CRect rect(prect);
  if (!CWnd::Create(NULL, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext))
    return FALSE;
  if (pParentWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
    ((CFrameWnd*)pParentWnd)->ModifyStyleEx(WS_EX_CLIENTEDGE,0,SWP_FRAMECHANGED);
    ((CFrameWnd*)pParentWnd)->RecalcLayout();
  }
  ResizeTab();
  return TRUE;
}

int TTabWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
  createFont();
	return 0;
}

void TTabWnd::OnDestroy() 
{
  CWnd::OnDestroy();
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator ++) {
    delete (*iterator);
  }
  m_TabList.clear();
  // This is done in TVisualFramework
  //if (GetParent()->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
  //  delete this;
}

// Virtual function to check whether switch to new tab can be done
BOOL TTabWnd::CanSetActivePane(CWnd *pOldPane, CWnd *pNewPane)
{
  return TRUE;
}

// Virtual function (after the switch is done)
void TTabWnd::OnSetActivePane(CWnd *pOldPane, CWnd *pNewPane)
{
}

// Create fonts for tab labels
void TTabWnd::createFont()
{
	NONCLIENTMETRICS metrics;
	metrics.cbSize = sizeof(metrics);
  ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0);

  CWindowDC wdc(NULL);
  int nLPixY = GetDeviceCaps(wdc.m_hDC, LOGPIXELSY);

	m_Font.CreateFontIndirect(&metrics.lfStatusFont);
}

// Add a tab to this window
TTabItem *TTabWnd::addTab(CWnd *pWnd, LPCTSTR szLabel)
{
  ASSERT(pWnd);
  ASSERT(szLabel);

  TTabItem *pItem = new TTabItem(this,szLabel);
  pItem->m_pWnd = pWnd;
  m_TabList.insert(m_TabList.end(), pItem);
  return pItem;
}

int TTabWnd::GetTabLength()
{
  int nLength = 0;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    nLength += (*iterator)->GetLength();
  }
  return nLength;
}

// Get index of currently selected tab
int TTabWnd::GetTabIndex(void)
{
  return m_nSelectedTab;
}

// Get number of tabs
int TTabWnd::GetTabCount(void)
{
  return m_TabList.size();
}

// Get index of the tab associated with specified window
int TTabWnd::GetTabIndex(CWnd *pWnd)
{
  ASSERT(pWnd);

  int nIndex = 0;
  TTabItem *pItem;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    pItem = *iterator;
    if (pItem->m_pWnd == pWnd)
      return nIndex;
    nIndex ++;
  }
  return -1;
}

// Get pointer to window associated with the specified tab index
CWnd *TTabWnd::GetTabWnd(int index)
{
  TTabItem *pItem = findTabItem(index);
  ASSERT(pItem);
  return ::IsWindow(pItem->m_pWnd->m_hWnd) ? pItem->m_pWnd : NULL;
}

// Get tab caption text of the specified tab
CString TTabWnd::GetTabLabel(int nIndex)
{
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  return pItem->GetText();
}

// Set text of tab caption
void TTabWnd::SetTabLabel(int nIndex, LPCTSTR szLabel)
{
  ASSERT(szLabel);
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  pItem->SetText(szLabel);
  invalidateTabArea();
}

// Enable/disable a view
void TTabWnd::Enable(int nIndex, BOOL bEnable)
{
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  pItem->Enable(bEnable);
  //pItem->m_pWnd->EnableWindow(bEnable);
}

// Cannot disable currently selected tab
void TTabWnd::EnableTab(int nIndex, BOOL bEnable)
{
  ASSERT(nIndex != m_nSelectedTab);
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  pItem->EnableTab(bEnable);
  invalidateTabArea();
}

// Cannot make invisible currently selected tab
void TTabWnd::ShowTab(int nIndex, BOOL bShow)
{
  ASSERT(nIndex != m_nSelectedTab);
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  pItem->ShowTab(bShow);
  invalidateTabArea();
}

// Is tab enabled
BOOL TTabWnd::IsTabEnabled(int nIndex)
{
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  return pItem->m_bEnabled;
}

// Is tab visible
BOOL TTabWnd::IsTabVisible(int nIndex)
{
  TTabItem *pItem = findTabItem(nIndex);
  ASSERT(pItem);
  return pItem->m_bVisible;
}

// Set font
void TTabWnd::SetFont(CFont *pFont)
{
  ASSERT(pFont);
  CWnd::SetFont(pFont);
  m_Font.DeleteObject();
  LOGFONT lf;
  pFont->GetLogFont(&lf);
  m_Font.CreateFontIndirect(&lf);
  invalidateTabArea();
}

// Set position of tabs (top or bottom)
void TTabWnd::SetTabPos(TTabPos nTabPos)
{
  m_nTabPos = nTabPos;
}

// Invalidate rectangle to redraw tabs
void TTabWnd::invalidateTabArea(void)
{
  CRect rect;
  switch (m_nTabPos) {
  case TP_TOP: 
    InvalidateRect(&CRect(0, 0, 32000, TABWND_HEIGHT)); 
    break;
  case TP_BOTTOM:
    GetClientRect(&rect);
    InvalidateRect(&CRect(CPoint(0,rect.Height()-TABWND_HEIGHT), 
                          CSize(32000,TABWND_HEIGHT)));
    break;
  };
}

// Draws a selected tab and returns its height
int TTabWnd::drawSelTabTop(CDC *pDC, int x, CRect& client, TTabItem *pItem)
{
  ASSERT(pItem);
  ASSERT(pDC);

  CString str = pItem->GetText();
  CSize textSize = pDC->GetTextExtent(str);
  textSize.cx += 10;
  if (textSize.cx > TAB_MAXLEN)
    textSize.cx = TAB_MAXLEN;
  int y = TABWND_HEIGHT - TABSEL_HEIGHT - TAB_DEPL;

  // black border, no bottom line
  pDC->SelectObject(&m_PenBlack);
  pDC->MoveTo(x,y+TABSEL_HEIGHT-1);
  pDC->LineTo(x,y);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y+TABSEL_HEIGHT);

  // left and upper border in white, double line
  pDC->SelectObject(&m_PenWhite2);
  pDC->MoveTo(x+2,y+TABSEL_HEIGHT-1);
  pDC->LineTo(x+2,y+2);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-4, y+2);

  // right border, dark gray, double line
  pDC->SelectObject(&m_PenDGray2);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y+2);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TABSEL_HEIGHT-1);

  // clean up
  pDC->SelectObject(&m_PenLGray);
  pDC->MoveTo(x-1, y+TABSEL_HEIGHT);
  pDC->LineTo(x+textSize.cx+TAB_SPACE, y+TABSEL_HEIGHT);
  pDC->MoveTo(x-1, y+TABSEL_HEIGHT+1);
  pDC->LineTo(x+textSize.cx+TAB_SPACE, y+TABSEL_HEIGHT+1);

  // a black line to far left and right
  pDC->SelectObject(&m_PenBlack);
  pDC->MoveTo(0, y+TABSEL_HEIGHT-1);
  pDC->LineTo(x, y+TABSEL_HEIGHT-1);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE+1, y+TABSEL_HEIGHT-1);
  pDC->LineTo(client.right, y+TABSEL_HEIGHT-1);

  // and a white double line
  pDC->SelectObject(&m_PenWhite2);
  if (x!=0) {
    pDC->MoveTo(0, y+TABSEL_HEIGHT+1);
    pDC->LineTo(x, y+TABSEL_HEIGHT+1);
  }
  pDC->MoveTo(x+textSize.cx+TAB_SPACE, y+TABSEL_HEIGHT+1);
  pDC->LineTo(client.right, y+TABSEL_HEIGHT+1);

  // gray inside
  pDC->FillSolidRect(x+3, y+3,textSize.cx+TAB_SPACE-6, TABSEL_HEIGHT, 
                  ::GetSysColor(COLOR_BTNFACE));

  CRect rect(CPoint(x+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2+1), textSize);
  pItem->SetFont(&m_Font);
  pItem->SetRect(rect);
  
  return textSize.cx+TAB_SPACE;
}

// Draw a selected tab at the bottom
int TTabWnd::drawSelTabBottom(CDC *pDC, int x, CRect& client, TTabItem *pItem)
{
  ASSERT(pItem);
  ASSERT(pDC);

  CString str = pItem->GetText();
  CSize textSize = pDC->GetTextExtent(str);
  textSize.cx += 10;
  if (textSize.cx > TAB_MAXLEN)
    textSize.cx = TAB_MAXLEN;

  int y = client.Height() - TABWND_HEIGHT + TAB_DEPL;

  // black border, no bottom line
  pDC->SelectObject(&m_PenBlack);
  pDC->MoveTo(x,y);
  pDC->LineTo(x,y+TABSEL_HEIGHT);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y+TABSEL_HEIGHT);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-1, y);

  // left border in white, double line
  pDC->SelectObject(&m_PenWhite2);
  pDC->MoveTo(x+2,y);
  pDC->LineTo(x+2,y+TABSEL_HEIGHT-1);

  // right and bottom border, dark gray, double line
  pDC->SelectObject(&m_PenDGray2);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-4, y+TABSEL_HEIGHT-1);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TABSEL_HEIGHT-1);

  // a black line to far left and right
  pDC->SelectObject(&m_PenBlack);
  pDC->MoveTo(0, y);
  pDC->LineTo(x, y);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE, y);
  pDC->LineTo(client.right, y);

  // and a gray line to far left and right
  pDC->SelectObject(&m_PenDGray);
  if (x != 0) {
    pDC->MoveTo(0, y-1);
    pDC->LineTo(x, y-1);
  }
  pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y-1);
  pDC->LineTo(client.right, y-1);

  // gray inside
  pDC->FillSolidRect(x+3,y,textSize.cx+TAB_SPACE-6, TABSEL_HEIGHT-2, 
                    ::GetSysColor(COLOR_BTNFACE));

  CRect rect(CPoint(x+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2), textSize);
  pItem->SetFont(&m_Font);
  pItem->SetRect(rect);
  
  return textSize.cx+TAB_SPACE;
}

// Draws an unselected tab and returs its height
int TTabWnd::drawTabTop(CDC *pDC, int x, CRect& client, TTabItem *pItem)
{
  ASSERT(pItem);
  ASSERT(pDC);

  CString str = pItem->GetText();
  CSize textSize = pDC->GetTextExtent(str);
  textSize.cx += 10;
  if (textSize.cx > TAB_MAXLEN)
    textSize.cx = TAB_MAXLEN;

  int y = TABWND_HEIGHT-TAB_HEIGHT-TAB_DEPL;
  
  // black border
  pDC->FrameRect(&CRect(CPoint(x,y), CSize(textSize.cx+TAB_SPACE, TAB_HEIGHT)), 
                  &m_BrushBlack);

  pDC->SelectObject(&m_PenWhite);
  pDC->MoveTo(x+1, y+1);
  pDC->LineTo(x+1, y+TAB_HEIGHT-1);
  pDC->MoveTo(x+1, y+1);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+1);

  pDC->SelectObject(&m_PenDGray);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y+1);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TAB_HEIGHT-1);

  pDC->FillRect(&CRect(CPoint(x+2,y+2), CSize(textSize.cx+TAB_SPACE-4, TAB_HEIGHT-3)), 
                &m_BrushLGray);

  // clean up
  int dy = TABSEL_HEIGHT-TAB_HEIGHT;
  pDC->FillSolidRect(x, y-dy, textSize.cx+TAB_SPACE, dy, GetSysColor(COLOR_BTNFACE));

  CRect rect(CPoint(x+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2+1), textSize);
  pItem->SetFont(&m_Font);
  pItem->SetRect(rect);
  
  return textSize.cx+TAB_SPACE;
}

// Draw an unselected tab at the bottom
int TTabWnd::drawTabBottom(CDC *pDC, int x, CRect& client, TTabItem *pItem)
{
  ASSERT(pItem);
  ASSERT(pDC);

  CString str = pItem->GetText();
  CSize textSize = pDC->GetTextExtent(str);
  textSize.cx += 10;
  if (textSize.cx > TAB_MAXLEN)
    textSize.cx = TAB_MAXLEN;

  int y = client.Height() - TABWND_HEIGHT + TAB_DEPL;

  // black border
  pDC->FrameRect(&CRect(CPoint(x,y), CSize(textSize.cx+TAB_SPACE, TAB_HEIGHT+1)), 
                  &m_BrushBlack);

  // Gray border bottom and right side
  pDC->SelectObject(&m_PenDGray);
  pDC->MoveTo(x+1, y+TAB_HEIGHT-1);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TAB_HEIGHT-1);
  pDC->MoveTo(x+textSize.cx+TAB_SPACE-2, y);
  pDC->LineTo(x+textSize.cx+TAB_SPACE-2, y+TAB_HEIGHT-1);

  pDC->FillRect(&CRect(CPoint(x+1,y+1), CSize(textSize.cx+TAB_SPACE-4, TAB_HEIGHT-3)), 
                &m_BrushLGray);

  CRect rect(CPoint(x+TAB_SPACE/2, y+(TAB_HEIGHT-textSize.cy)/2), textSize);
  pItem->SetFont(&m_Font);
  pItem->SetRect(rect);
  
  return textSize.cx+TAB_SPACE;
}

// Draw edge arround client area
void TTabWnd::drawClient(CDC *pDc, CRect& rect)
{
  ASSERT(pDc);

  CWnd *pParent = GetParent();
  ASSERT(pParent);
  switch (m_nTabPos) {
  case TP_TOP:
    if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
      pDc->DrawEdge(&rect, EDGE_ETCHED, BF_TOP);
    }
    pDc->Draw3dRect(0,TABWND_HEIGHT, rect.right, rect.bottom-TABWND_HEIGHT,
                   ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHIGHLIGHT));
    pDc->Draw3dRect(1,TABWND_HEIGHT+1, rect.right-2, rect.bottom-TABWND_HEIGHT-2,
                    0, ::GetSysColor(COLOR_3DLIGHT));
    break;
  case TP_BOTTOM:
    if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
      pDc->DrawEdge(&rect, EDGE_ETCHED, BF_BOTTOM);
    }
    pDc->Draw3dRect(0,0, rect.right, rect.bottom-TABWND_HEIGHT+1,
                   ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHIGHLIGHT));
    pDc->Draw3dRect(1,1, rect.right-2, rect.bottom-TABWND_HEIGHT-1,
                    0, ::GetSysColor(COLOR_3DLIGHT));
    break;
  }
}

void TTabWnd::OnPaint()
{
  CPaintDC dc(this); // device context for painting
  CRect client;

  GetClientRect(&client);
  drawClient(&dc, client);

  int x = 0, nIndex = 0;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    TTabItem *pItem = *iterator;
    ASSERT(pItem != NULL);
    if (pItem->m_bVisible) {
      pItem->m_nMinX = x;
      if (nIndex != m_nSelectedTab) {
        switch (m_nTabPos) {
        case TP_TOP: x += drawTabTop(&dc, x, client, pItem); break;
        case TP_BOTTOM: x += drawTabBottom(&dc, x, client, pItem); break;
        }
      } else {
        switch (m_nTabPos) {
        case TP_TOP: x += drawSelTabTop(&dc, x, client, pItem); break;
        case TP_BOTTOM: x += drawSelTabBottom(&dc, x, client, pItem); break;
        }
      }
    }
    pItem->m_nMaxX = x;
    nIndex ++;
  }
}

// Returns tab index that holds specified point
int TTabWnd::HitTest(CPoint& point)
{
  return HitTest(point.x,point.y);
}

// Returns tab index that holds specified point
int TTabWnd::HitTest(int x, int y)
{
  int notsel_y_min, sel_y_min, y_max;
  CRect rect;

  GetClientRect(&rect);
  switch (m_nTabPos) {
  case TP_TOP:
    notsel_y_min = TABWND_HEIGHT - TAB_HEIGHT - TAB_DEPL;
    sel_y_min = TABWND_HEIGHT - TABSEL_HEIGHT - TAB_DEPL;
    y_max = TABWND_HEIGHT - TAB_DEPL;
    break;
  case TP_BOTTOM:
    notsel_y_min = rect.Height() - TABWND_HEIGHT + TAB_DEPL;
    sel_y_min = rect.Height() - TABWND_HEIGHT + TAB_DEPL;
    y_max = rect.Height() - TABWND_HEIGHT + TAB_DEPL + TAB_HEIGHT;
    break;
  };

  int nIndex = 0;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    TTabItem *pItem = (*iterator);
    if (pItem->m_bEnabled && pItem->m_bVisible) {
      if (nIndex != m_nSelectedTab && (y < notsel_y_min || y > y_max)) 
        continue;
      if (nIndex == m_nSelectedTab && (y < sel_y_min || y > y_max)) 
        continue;
      if (x >= pItem->m_nMinX && x <= pItem->m_nMaxX)
        return nIndex;
    }
    nIndex++;
  }
  return -1;
}

// Switch focus to specified tab index
BOOL TTabWnd::SetActivePane(int nIndex, BOOL bActivate)
{
  if (nIndex == -1)
    return FALSE;
  if (nIndex == m_nSelectedTab)
    return TRUE;

  TTabItem *pNewPane = findTabItem(nIndex);
  if (!pNewPane->m_bEnabled || !pNewPane->m_bVisible)
    return FALSE;
  TTabItem *pOldPane = NULL;
  if (m_nSelectedTab != -1)
    pOldPane = findTabItem(m_nSelectedTab);
  if (CanSetActivePane(pOldPane ? pOldPane->m_pWnd : NULL, pNewPane->m_pWnd)) {
    // Deactivate old pane
    if (m_nSelectedTab != -1) {
      pOldPane->m_pWnd->EnableWindow(FALSE);
      pOldPane->m_pWnd->ShowWindow(SW_HIDE);
    }
    // Activate new pane
    pNewPane->m_pWnd->EnableWindow(pNewPane->m_bWndEnabled ? TRUE : FALSE);
    pNewPane->m_pWnd->ShowWindow(SW_SHOW);
    pNewPane->m_pWnd->SetFocus();
    // Save index of new pane
    m_nSelectedTab = nIndex;
    // Invalidate tab
    invalidateTabArea();
    // Inform derived class
    OnSetActivePane(pOldPane ? pOldPane->m_pWnd : NULL, pNewPane->m_pWnd);
    // Update frame window
    if (bActivate) {
      CWnd *pParent = GetParent();
      while (pParent && !pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
        pParent = pParent->GetParent();
      ASSERT(pParent != NULL);
      updateFrame((CFrameWnd*)pParent, pNewPane->m_pWnd);
    }
    return TRUE;
  }
  return FALSE;
}

// Update frame window active view based on newly selected tab item
BOOL TTabWnd::updateFrame(CFrameWnd *pFrame, CWnd *pWnd)
{
  ASSERT(pFrame);
  ASSERT(pWnd);

  if (pWnd->IsKindOf(RUNTIME_CLASS(CView))) {
    // New tab item is a view
    pFrame->SetActiveView((CView*)pWnd);
    return TRUE;
  } else if (pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
    CSplitterWnd *pSplitter = (CSplitterWnd*)pWnd;
    CWnd *pView = pSplitter->GetActivePane();
    if (pView == NULL) {
      CWnd *pTmpView;
      for (int x = 0; x < pSplitter->GetRowCount(); x ++) {
        for (int y = 0; y < pSplitter->GetColumnCount(); y ++) {
          pTmpView = pSplitter->GetPane(x,y);
          if (pTmpView->IsWindowEnabled()) {
            if (updateFrame(pFrame, pTmpView))
              return TRUE;
          }
        }
      }
    }
    if (pView == NULL)
      pView = pSplitter->GetPane(0,0);
    pFrame->SetActiveView((CView*)pView);
  } else if (pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pWnd;
    int nIndex = pTab->GetTabIndex();
    CWnd *pTabWnd = pTab->GetTabWnd(nIndex);
    if (updateFrame(pFrame, pTabWnd))
      return TRUE;
  }
  return FALSE;
}

// Resize tab
void TTabWnd::ResizeTab(int cx, int cy)
{
  CRect rect;
  CWnd *pParent = (CWnd*)GetParent();
  ASSERT(pParent);
  pParent->GetClientRect(&rect); 

  if (pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
    CSplitterWnd *splitter = (CSplitterWnd*)pParent;
    ASSERT(pParent);
    int row,col;
    splitter->IsChildPane(this,row,col);
    splitter->RecalcLayout();
  } else if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
    m_bLockFlag = TRUE;
    pParent->RepositionBars(0, 0xFFFF, AFX_IDW_PANE_FIRST, CWnd::reposQuery, &rect);
    MoveWindow(rect.left,rect.top,rect.Width(),rect.Height());
    m_bLockFlag = FALSE;
  }

  m_bLockFlag = TRUE; // reentrancy check (might get called recursivly from OnSize)
  CWnd *pWnd;
  TTabItemList::iterator iterator;
  for (iterator = m_TabList.begin(); iterator != m_TabList.end(); iterator++) {
    pWnd = (*iterator)->m_pWnd;
    if (cx == -1 && cy == -1) {
      switch (m_nTabPos) {
      case TP_TOP:
        pWnd->MoveWindow(1, TABWND_HEIGHT+1, 
                         rect.Width()-2, rect.Height()-TABWND_HEIGHT-2);
        break;
      case TP_BOTTOM:
        pWnd->MoveWindow(1, 0, rect.Width()-2, rect.Height()-TABWND_HEIGHT);
        break;
      }
    } else {
      switch (m_nTabPos) {
      case TP_TOP:
        pWnd->MoveWindow(1, TABWND_HEIGHT+1, cx, cy-TABWND_HEIGHT-2);
        break;
      case TP_BOTTOM:
        pWnd->MoveWindow(1, 0, cx, cy-TABWND_HEIGHT);
        break;
      }
    }
  }
  m_bLockFlag=FALSE;
}

// Erase area where the tabs are displayed
BOOL TTabWnd::OnEraseBkgnd(CDC* pDC)
{
  ASSERT(pDC);

  CRect rect;
  GetClientRect(&rect);
  switch (m_nTabPos) {
  case TP_TOP:
    pDC->FillSolidRect(&CRect(0, 0, rect.right, TABWND_HEIGHT), 
                      ::GetSysColor(COLOR_BTNFACE));
    break;
  case TP_BOTTOM:
    pDC->FillSolidRect(&CRect(0, rect.bottom-TABWND_HEIGHT-3, rect.right, rect.bottom), 
                      ::GetSysColor(COLOR_BTNFACE));
    break;
  }
  return TRUE;
}

// Handle couse click on tabs
void TTabWnd::OnLButtonUp(UINT nFlags, CPoint point)
{
  int nNewTab = HitTest(point.x, point.y);
  SetActivePane(nNewTab);
  CWnd::OnLButtonUp(nFlags, point);
}

// Handle resize
LRESULT TTabWnd::OnSizeParent(WPARAM, LPARAM lParam)
{
  if (m_bLockFlag)
    return 0;
  ResizeTab();
  return 0;
}

// Handle resize
void TTabWnd::OnSize(UINT nType, int cx, int cy) 
{
  CWnd::OnSize(nType, cx, cy);
  ResizeTab(cx,cy);
}

// Create a CView derived class as a tab
TTabItem *TTabWnd::CreatePane(LPCTSTR lpszLabel, CRuntimeClass *pViewClass, 
                                 CCreateContext *pContext)
{
  CRect rect, client;
  ASSERT(pViewClass && pContext);

  CWnd *pWnd = (CWnd*)pViewClass->CreateObject();
  if (!pWnd) 
    return NULL;
  
  GetClientRect(&client);
  rect.left = 0;
  rect.top = TABWND_HEIGHT+2;
  rect.right = client.right;
  rect.bottom = client.bottom;

  int dwStyle = AFX_WS_DEFAULT_VIEW;
  if (GetParent()->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
    dwStyle &= ~WS_BORDER;
  }

  if (!pWnd->Create(NULL, NULL, dwStyle, rect, this, 13576+m_TabList.size(), pContext))
  {
    TRACE0("Warning: couldn't create client area for tab view\n");
    // pWnd will be cleaned up by PostNcDestroy
    return NULL;
  }

  // Insert new tab object into the list
  TTabItem *pTab = addTab(pWnd,lpszLabel);
  ASSERT(pTab);
  if (m_TabList.size() != 1) {
    pWnd->EnableWindow(FALSE);
    pWnd->ShowWindow(SW_HIDE);
  /*
  // Framework is responsible to set the active view
  } else {
    CWnd *pParent = GetParent();
    if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
      ((CFrameWnd*)pParent)->SetActiveView((CView*)pWnd);
    } else if (pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
      ((CSplitterWnd*)pParent)->SetActivePane(0,0,pWnd);
    }
  */
  }
  return pTab;
}

// Create a splitter window as a tab
TTabItem *TTabWnd::CreatePane(LPCTSTR lpszLabel, int nRows, int nCols, 
                                 CWnd *pWnd, UINT nID)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));

  // Moved to TVisualFramework to handle creation of CSplitterWnd derived classes
  //CSplitterWnd *pWnd = new CSplitterWnd;
  //if (!pWnd) 
  //  return NULL;

  int dwStyle = AFX_WS_DEFAULT_VIEW;
  dwStyle &= ~WS_BORDER;

  CSplitterWnd *pSplitter = (CSplitterWnd*)pWnd;
  if (!pSplitter->CreateStatic(this, nRows, nCols, dwStyle, nID)) {
    TRACE0("Warning: couldn't create client area for tab view\n");
    // pWnd will be cleaned up by PostNcDestroy
    return NULL;
  }

  TTabItem *pTab = addTab(pWnd,lpszLabel);
  ASSERT(pTab);
  if (m_TabList.size() != 1) {
    pWnd->EnableWindow(FALSE);
    pWnd->ShowWindow(SW_HIDE);
  } 

  /*
  // Framework will set the active view
  CWnd *paneWnd = pWnd->GetActivePane();
  if (paneWnd) {
    ((CFrameWnd*)GetParent())->SetActiveView((CView*)paneWnd);
  } else {
    paneWnd = pWnd->GetPane(0,0);
    pWnd->SetActivePane(0,0);
    ((CFrameWnd*)GetParent())->SetActiveView((CView*)paneWnd);
  }
  */

  return pTab;
}

//=============================================================================
// class TVisualObject
//
//=============================================================================

// Private constructor
TVisualObject::TVisualObject()
{
}

// Create a plain view
TVisualObject::TVisualObject(DWORD dwId, CCreateContext *pContext, 
                       CRuntimeClass *pClass)
{
  ASSERT(pContext);
  ASSERT(pClass);
  ASSERT(pClass->IsDerivedFrom(RUNTIME_CLASS(CView)));

  zeroAll();
  m_dwId = dwId;
  m_nObjectType = OT_VIEW;
  m_pContext = pContext;
  m_pRuntimeClass = pClass;
  checkStyle();
}

// Create a view within a tab window or a tab window
TVisualObject::TVisualObject(DWORD dwId, LPCTSTR szTitle, CCreateContext *pContext, 
                       CRuntimeClass *pClass, DWORD dwStyle)
{
  ASSERT(szTitle);
  ASSERT(pContext);
  ASSERT(pClass);

  zeroAll();
  m_dwId = dwId;
  if (pClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd))) {
    m_nObjectType = OT_TAB;
  } else if (pClass->IsDerivedFrom(RUNTIME_CLASS(CView))) {
    m_nObjectType = OT_TABVIEW;
  } else {
    ASSERT(FALSE);
  }
  m_strTitle = szTitle;
  m_pContext = pContext;
  m_pRuntimeClass = pClass;
  m_dwStyle = dwStyle;
  checkStyle();
}

// Create a splitter window
TVisualObject::TVisualObject(DWORD dwId, LPCTSTR szTitle, int nRows, int nCols, 
                       CCreateContext *pContext, DWORD dwStyle)
{
  ASSERT(szTitle);
  ASSERT(pContext);
  ASSERT(nRows);
  ASSERT(nCols);

  zeroAll();
  m_dwId = dwId;
  m_nObjectType = OT_SPLITTER;
  m_strTitle = szTitle;
  m_pContext = pContext;
  m_nRows = nRows;
  m_nCols = nCols;
  m_dwStyle = dwStyle;
  checkStyle();
}

// Create a view within a splitter window
TVisualObject::TVisualObject(DWORD dwId, int nRow, int nCol, CCreateContext *pContext, 
                       CRuntimeClass *pClass, CSize size, DWORD dwStyle)
{
  ASSERT(pContext);
  ASSERT(pClass);
  ASSERT(pClass->IsDerivedFrom(RUNTIME_CLASS(CView)) ||
         pClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd)));

  zeroAll();
  m_dwId = dwId;
  m_nObjectType = OT_SPLITTERVIEW;
  m_pContext = pContext;
  m_pRuntimeClass = pClass;
  m_nRowIndex = nRow;
  m_nColIndex = nCol;
  m_Size = size;
  m_dwStyle = dwStyle;
  checkStyle();
}

// Create a splitter within a splitter window
TVisualObject::TVisualObject(DWORD dwId, int nRow, int nCol, int nRows, int nCols, 
                       CCreateContext *pContext, DWORD dwStyle)
{
  ASSERT(pContext);
  ASSERT(nRows);
  ASSERT(nCols);

  zeroAll();
  m_dwId = dwId;
  m_nObjectType = OT_SPLITTERSPLITTER;
  m_pContext = pContext;
  m_nRowIndex = nRow;
  m_nColIndex = nCol;
  m_nRows = nRows;
  m_nCols = nCols;
  m_dwStyle = dwStyle;
  checkStyle();
}

TVisualObject::TVisualObject(const TVisualObject& obj)
{
  zeroAll();
  *this = obj;
}

TVisualObject::~TVisualObject()
{
}

TVisualObject& TVisualObject::operator=(const TVisualObject& obj)
{
  // No need to copy m_ObjectList since it is populated after
  // this code is executed in STL container
  m_nObjectType = obj.m_nObjectType;
  m_dwId = obj.m_dwId;
  m_pWnd = obj.m_pWnd;
  m_pParent = obj.m_pParent;
  m_strTitle = obj.m_strTitle;
  m_nRows = obj.m_nRows;
  m_nCols = obj.m_nCols;
  m_nRowIndex = obj.m_nRowIndex;
  m_nColIndex = obj.m_nColIndex;
  m_pContext = obj.m_pContext;
  m_pRuntimeClass = obj.m_pRuntimeClass;
  m_Size = obj.m_Size;
  m_bEnabled = obj.m_bEnabled;
  m_dwStyle = obj.m_dwStyle;
  m_cHotKey = obj.m_cHotKey;
  m_pOwner = obj.m_pOwner;
  m_pFramework = obj.m_pFramework;
  return *this;
}

void TVisualObject::zeroAll(void)
{
  // No need to zero m_ObjectList since it is already empty
  m_nObjectType = OT_UNKNOWN;
  m_dwId = 0;
  m_pWnd = NULL;
  m_pParent = NULL;
  m_strTitle = _T("");
  m_nRows = 0;
  m_nCols = 0;
  m_nRowIndex = 0;
  m_nColIndex = 0;
  m_pContext = NULL;
  m_pRuntimeClass = NULL;
  m_Size = CSize(0,0);
  m_bEnabled = TRUE;
  m_dwStyle = 0;
  m_cHotKey = 0;
  m_pOwner = NULL;
  m_pFramework = NULL;
}

// Check if style is valid
void TVisualObject::checkStyle(void)
{
  if ((m_dwStyle & TOS_TABTOP) || (m_dwStyle & TOS_TABBOTTOM)) {
    ASSERT(m_pRuntimeClass);
    // Tab position valid only for tab window derived classes
    ASSERT(m_pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd)));
  }
  if (m_dwStyle & TOS_SELECTED) {
    // Selected valid only for tab panes that are not splitters and tabs
    // In this case, use TVisualFramework::SetActivePane() to set the active pane
    // once the framework is created
    if (m_pRuntimeClass == NULL) {
      // Splitters canot be dynamically create (m_pRuntimeClass is NULL)
      ASSERT((m_nObjectType != OT_SPLITTER) && (m_nObjectType != OT_SPLITTERVIEW));
    } else {
      ASSERT(!m_pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(TTabWnd)));
    }
  }
}

// Delete the window pointer and optionally destroy the window
void TVisualObject::Destroy(BOOL bDestroyWindow)
{
  if (m_pWnd) {
    if (bDestroyWindow)
      m_pWnd->DestroyWindow();
    delete m_pWnd;
    m_pWnd = NULL;
  }
}

// If this object is a tab window or splitter window that it 
// cannot be focused
BOOL TVisualObject::CanFocus(void)
{
  ASSERT(m_pWnd);

  if (m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)) ||
      m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
  {
    return FALSE;
  }
  return TRUE;
}

// Set hot key for this tab object
void TVisualObject::SetHotKey(CHAR cHotKey)
{
  m_cHotKey = cHotKey;
}

// Optional: Set description 
void TVisualObject::SetDescription(LPCTSTR szDesc)
{
  m_strDescription = szDesc;
}

// Set this object as active pane
BOOL TVisualObject::SetActivePane(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->SetActivePane(this);
}

// Set this tab to be active tab (not the active pane)
BOOL TVisualObject::SetActiveTab(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->SetActiveTab(this);
}

// Enable/disable this object
BOOL TVisualObject::Enable(BOOL bEnable)
{
  ASSERT(m_pFramework);
  return m_pFramework->Enable(this,bEnable);
}

// Enable/disable tab
BOOL TVisualObject::EnableTab(BOOL bEnable)
{
  ASSERT(m_pFramework);
  return m_pFramework->EnableTab(this,bEnable);
}

// SHow/hide this object
BOOL TVisualObject::ShowTab(BOOL bShow)
{
  ASSERT(m_pFramework);
  return m_pFramework->ShowTab(this,bShow);
}

// Is this object enabled
BOOL TVisualObject::IsEnabled(BOOL& bEnabled)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsEnabled(this,bEnabled);
}

// Is this object enabled
BOOL TVisualObject::IsTabEnabled(BOOL& bEnabled)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsTabEnabled(this,bEnabled);
}

// Is this object visible
BOOL TVisualObject::IsTabVisible(BOOL& bVisible)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsTabVisible(this,bVisible);
}

// Returns TRUE if this object is a tab within a tab window
BOOL TVisualObject::IsTabPane(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsTabPane(this);
}

// Returns TRUE if this object is a tab window
BOOL TVisualObject::IsTabWindow(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsTabWindow(this);
}

// Returns TRUE if this object is a pane within a splitter window
BOOL TVisualObject::IsSplitterPane(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsSplitterPane(this);
}

// Returns TRUE if this object is a splitter window
BOOL TVisualObject::IsSplitterWindow(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsSplitterWindow(this);
}

// Returns TRUE if this object is derived from CView 
BOOL TVisualObject::IsView(void)
{
  ASSERT(m_pFramework);
  return m_pFramework->IsView(this);
}

// Get object ID
#ifdef _DEBUG
DWORD TVisualObject::GetID(void)
{
  return m_dwId;
}
#endif

// Get object window
#ifdef _DEBUG
CWnd *TVisualObject::GetWnd(void)
{
  return m_pWnd;
}
#endif

// Get safe object window
#ifdef _DEBUG
CWnd *TVisualObject::GetSafeWnd(void)
{
  return ::IsWindow(m_pWnd->m_hWnd) ? m_pWnd : NULL;
}
#endif

#ifdef _DEBUG
CString TVisualObject::GetTitle(void)
{
  return m_strTitle;
}
#endif

#ifdef _DEBUG
CString TVisualObject::GetDescription(void)
{
  return m_strDescription;
}
#endif

#ifdef _DEBUG
CWnd *TVisualObject::GetParentWnd(void)
{
  return m_pParent;
}
#endif

#ifdef _DEBUG
TVisualFramework *TVisualObject::GetFramework(void)
{
  return m_pFramework;
}
#endif

#ifdef _DEBUG
TVisualObject *TVisualObject::GetOwner(void)
{
  return m_pOwner;
}
#endif

//=============================================================================
// class TVisualFramework
//
//=============================================================================

IMPLEMENT_DYNCREATE(TVisualFramework, CCmdTarget)

BEGIN_MESSAGE_MAP(TVisualFramework, CCmdTarget)
	//{{AFX_MSG_MAP(TVisualFramework)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

TVisualFramework::TVisualFramework()
{
  m_pOwner = NULL;
  m_bEnableCtrlTab = TRUE;
}

TVisualFramework::~TVisualFramework()
{
  if (m_ObjectMap.size() && m_ObjectList.size()) {
    TRACE0(_T(">>> TVisualFramework::Destroy() called in TVisualFramework destructor\n"));
    TRACE0(_T(">>>   It must be called in CFrameWnd derived class OnDestroy() message handler\n"));
    Destroy();
  }
}

// Find an object in the map with the specified unique id
TVisualObject *TVisualFramework::findObject(DWORD dwId)
{
  TVisualObjectMap::iterator iterator;
  for (iterator = m_ObjectMap.begin(); iterator != m_ObjectMap.end(); iterator++) {
    if (dwId == iterator->first)
      return iterator->second;
  }
  return NULL;
}

// Find an object in the map with the specified window
TVisualObject *TVisualFramework::findObject(CWnd *pWnd)
{
  TVisualObjectMap::iterator iterator;
  for (iterator = m_ObjectMap.begin(); iterator != m_ObjectMap.end(); iterator++) {
    if (pWnd == iterator->second->m_pWnd)
      return iterator->second;
  }
  return NULL;
}

// Add object to the container (this is a root level object)
// There is only one root level object (either splitter or tab)
BOOL TVisualFramework::Add(TVisualObject *pObject)
{
  ASSERT(pObject);
  ASSERT(m_ObjectList.size() == 0);   // Only one root level object allowed

  // Root level object is either a view, splitter or a tab
  ASSERT((pObject->m_nObjectType == TVisualObject::OT_TAB) || 
         (pObject->m_nObjectType == TVisualObject::OT_VIEW) || 
         (pObject->m_nObjectType == TVisualObject::OT_SPLITTER));

  if (findObject(pObject->m_dwId) == NULL) {
    m_ObjectList.insert(m_ObjectList.end(), pObject);
    pObject->m_pFramework = this;
    m_ObjectMap[pObject->m_dwId] = pObject;
    return TRUE;
  }
  ASSERT(FALSE);    // Duplicate object Id
  return FALSE;
}

// Add child object to the specified object
BOOL TVisualFramework::Add(TVisualObject *pOwner, TVisualObject *pObject)
{
  ASSERT(pObject);

  #ifdef _DEBUG
  // Validate definition
  if (pOwner->m_nObjectType == TVisualObject::OT_TAB) {
    if ((pObject->m_nObjectType != TVisualObject::OT_TABVIEW) && 
        (pObject->m_nObjectType != TVisualObject::OT_SPLITTER) &&
        (pObject->m_nObjectType != TVisualObject::OT_TAB))
    {
      ASSERT(FALSE);
    }
  } else if (pOwner->m_nObjectType == TVisualObject::OT_SPLITTER) {
    if ((pObject->m_nObjectType != TVisualObject::OT_SPLITTERVIEW) && 
        (pObject->m_nObjectType != TVisualObject::OT_SPLITTERSPLITTER))
    {
      ASSERT(FALSE);
    }
  } else if (pOwner->m_nObjectType == TVisualObject::OT_SPLITTERSPLITTER) {
    if ((pObject->m_nObjectType != TVisualObject::OT_SPLITTERVIEW) && 
        (pObject->m_nObjectType != TVisualObject::OT_SPLITTERSPLITTER))
    {
      ASSERT(FALSE);
    }
  } else if (pOwner->m_nObjectType == TVisualObject::OT_TABVIEW) {
    if ((pObject->m_nObjectType != TVisualObject::OT_SPLITTER))
    {
      ASSERT(FALSE);
    }
  }

  #endif

  if (findObject(pObject->m_dwId) == NULL) {
    pOwner->m_ObjectList.insert(pOwner->m_ObjectList.end(), pObject);
    pObject->m_pOwner = pOwner;
    pObject->m_pFramework = this;
    m_ObjectMap[pObject->m_dwId] = pObject;
    return TRUE;
  }
  ASSERT(FALSE);    // Duplicate object Id
  return FALSE;
}

// Create all objects within the framework
BOOL TVisualFramework::Create(CWnd *pWnd)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)));

  // Save owner for later
  m_pOwner = pWnd;

  // Disable Ctrl+Tab for MDI applications
  if (pWnd->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)))
    m_bEnableCtrlTab = FALSE;

  // Walk thru visual object hierarchy and create windows
  BOOL rc;
  TVisualObject *pObject;
  TVisualObjectList::iterator it;
  for (it = m_ObjectList.begin(); it != m_ObjectList.end(); it++) {
    pObject = *it;
    rc = execCreate(pWnd, pObject);
    if (rc == FALSE) {
      TRACE0(_T("Create visual object failed!\n"));
      return FALSE;
    }
  }
  
  // Walk thru the map and find first object that can be focused
  // Then set focus
  TVisualObjectMap::iterator mapit;
  for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit ++) {
    pObject = mapit->second;
    if (pObject->CanFocus()) {
      SetActivePane(pObject);
      break;
    }
  }

  return TRUE;
}

// Destroy all objects in the framework
void TVisualFramework::Destroy(void)
{
  TVisualObject *pObject;

  // Recursive delete of all objects
  TVisualObjectList::iterator it;
  for (it = m_ObjectList.begin(); it != m_ObjectList.end(); it++) {
    pObject = *it;
    execDestroy(pObject);
  }

  // Delete pointers in object map
  TVisualObjectMap::iterator mapit;
  for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit++) {
    pObject = mapit->second;
    delete pObject;
  }

  // Empty all containers (for check in destructor)
  m_ObjectMap.clear();
  m_ObjectList.clear();
}

// Recursive function to delete all object windows. Does not delete views since
// they are destroyed by frame.
void TVisualFramework::execDestroy(TVisualObject *pObject)
{
  if (pObject->m_pWnd && ::IsWindow(pObject->m_pWnd->m_hWnd)) {
    if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
      TVisualObject *pObj;
      TVisualObjectList::iterator it;
      for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
        pObj = *it;
        execDestroy(pObj);
      }
      pObject->Destroy(TRUE);
    } else if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) {
      TVisualObject *pObj;
      TVisualObjectList::iterator it;
      for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
        pObj = *it;
        execDestroy(pObj);
      }
      pObject->Destroy(TRUE);
    } 
  }
}

// Create specified object and all its childs
BOOL TVisualFramework::execCreate(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pObject);
  
  BOOL rc = FALSE;
  switch (pObject->m_nObjectType) {
  case (TVisualObject::OT_SPLITTER): 
    rc = execCreateSplitter(pWnd,pObject);
    break;
  case (TVisualObject::OT_SPLITTERVIEW): 
    rc = execCreateSplitterView(pWnd, pObject);
    break;
  case (TVisualObject::OT_SPLITTERSPLITTER):
    rc = execCreateSplitterSplitter(pWnd, pObject);
    break;
  case (TVisualObject::OT_TAB):
    rc = execCreateTabWnd(pWnd, pObject);
    break;
  case (TVisualObject::OT_TABVIEW):
    rc = execCreateTabView(pWnd, pObject);
    break;
  case (TVisualObject::OT_VIEW):
    rc = execCreateView(pWnd, pObject);
    break;
  }
  return rc;
}

// Create a simple view 
BOOL TVisualFramework::execCreateView(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)));
  ASSERT(pObject);
  ASSERT(pObject->m_pContext);
  ASSERT(pObject->m_pRuntimeClass);

  CFrameWnd *pFrame = (CFrameWnd*)pWnd;
  pObject->m_pContext->m_pNewViewClass = pObject->m_pRuntimeClass;
  pObject->m_pWnd = pFrame->CreateView(pObject->m_pContext);
  ASSERT(pObject->m_pWnd);
  ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  
  pObject->m_pParent = pFrame;
  setTabWndProperties(pObject);
  
  return TRUE;
}

// Create a view within a tab window
BOOL TVisualFramework::execCreateTabView(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)));
  ASSERT(pObject);
  ASSERT(pObject->m_pContext);
  ASSERT(pObject->m_pRuntimeClass);
  ASSERT(!pObject->m_strTitle.IsEmpty());

  TTabWnd *pTab = (TTabWnd*)pWnd;
  TTabItem *pItem;
  pItem = pTab->CreatePane(pObject->m_strTitle, pObject->m_pRuntimeClass, 
                            pObject->m_pContext);
  ASSERT(pItem);
  pObject->m_pWnd = pItem->GetSafeWnd();
  ASSERT(pObject->m_pWnd);
  ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  pObject->m_pParent = pTab;
  setTabWndProperties(pObject);
  return TRUE;
}

// Create a splitter window
BOOL TVisualFramework::execCreateSplitter(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pObject);

  // Cannot use pObject->m_pRuntimeClass->CreateObject() since splitters 
  // do not support dynamic creation
  pObject->m_pWnd = CreateSplitter(pObject->m_dwId);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));

  if (pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pWnd;
    TTabItem *pItem = pTab->CreatePane(pObject->m_strTitle, pObject->m_nRows, 
                                       pObject->m_nCols, pObject->m_pWnd);
    ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
    pObject->m_pParent = pWnd;
  } else if (pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))) {
    ((CSplitterWnd*)pObject->m_pWnd)->CreateStatic(pWnd,pObject->m_nRows,pObject->m_nCols);
    ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
    pObject->m_pParent = pWnd;
  }
  setTabWndProperties(pObject);
  TVisualObjectList::iterator it;
  for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
    execCreate(pObject->m_pWnd, *it);
  }
  return TRUE;
}

// Create a view within a splitter. Then create all childs of this view.
BOOL TVisualFramework::execCreateSplitterView(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  ASSERT(pObject);
  
  CSplitterWnd *pSplitter = (CSplitterWnd*)pWnd;
  pSplitter->CreateView(pObject->m_nRowIndex, pObject->m_nColIndex, 
                        pObject->m_pRuntimeClass, pObject->m_Size, 
                        pObject->m_pContext);
  pObject->m_pWnd = pSplitter->GetPane(pObject->m_nRowIndex, pObject->m_nColIndex);
  ASSERT(pObject->m_pWnd);
  ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
  pObject->m_pParent = pSplitter;
  setTabWndProperties(pObject);
  TVisualObjectList::iterator it;
  for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
    execCreate(pObject->m_pWnd, *it);
  }
  return TRUE;
}

// Create a nested splitter window
BOOL TVisualFramework::execCreateSplitterSplitter(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  
  CSplitterWnd *pParent = (CSplitterWnd*)pWnd;
  // Cannot use pObject->m_pRuntimeClass->CreateObject() since splitters 
  // do not support dynamic creation
  pObject->m_pWnd = CreateSplitter(pObject->m_dwId);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)));
  CSplitterWnd *pSplitter = (CSplitterWnd*)pObject->m_pWnd;
  pSplitter->CreateStatic(pParent, pObject->m_nRows, pObject->m_nCols, 
                          WS_CHILD|WS_VISIBLE|WS_BORDER, 
                          pParent->IdFromRowCol(pObject->m_nRowIndex,pObject->m_nColIndex));
  ASSERT(::IsWindow(pSplitter->m_hWnd));
  pObject->m_pParent = pParent;
  TVisualObjectList::iterator it;
  for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
    execCreate(pObject->m_pWnd, *it);
  }
  return TRUE;
}

// Create a tab window and all its childs (tabs)
BOOL TVisualFramework::execCreateTabWnd(CWnd *pWnd, TVisualObject *pObject)
{
  ASSERT(pWnd);
  ASSERT(pObject);
  
  if (pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pWnd;
    TTabItem *pItem = pTab->CreatePane(pObject->m_strTitle, pObject->m_pRuntimeClass,
                                       pObject->m_pContext);
    ASSERT(pItem);
    pObject->m_pWnd = pItem->GetSafeWnd();
    ASSERT(pObject->m_pWnd);
    ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
    pObject->m_pParent = pWnd;
    setTabWndProperties(pObject);
  } else {
    CRect rect;
    pObject->m_pWnd = (CWnd*)pObject->m_pRuntimeClass->CreateObject();
    ASSERT(pObject->m_pWnd);
    pObject->m_pParent = pWnd;
    pObject->m_pWnd->Create(NULL,_T(""),WS_VISIBLE|WS_CHILD,
                                rect,pWnd,TABWND_DEFAULT_ID);
    ASSERT(::IsWindow(pObject->m_pWnd->m_hWnd));
    setTabWndProperties(pObject);
  }
  TVisualObject *pObj;
  TVisualObjectList::iterator it;
  for (it = pObject->m_ObjectList.begin(); it != pObject->m_ObjectList.end(); it++) {
    pObj = *it;
    execCreate(pObject->m_pWnd, pObj);
  }
  return TRUE;
}

// Set properties of tab window
void TVisualFramework::setTabWndProperties(TVisualObject *pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd != NULL);

  // If this is a tab window then set the position of tabs
  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pObject->m_pWnd;
    if (pObject->m_dwStyle & TVisualObject::TOS_TABTOP)
      pTab->SetTabPos(TTabWnd::TP_TOP);
    else if (pObject->m_dwStyle & TVisualObject::TOS_TABBOTTOM)
      pTab->SetTabPos(TTabWnd::TP_BOTTOM);
  }
  
  // If this is a pane within a tab then check if this pane
  // should be a selected pane
  ASSERT(pObject->m_pParent);
  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
    if (pObject->m_dwStyle & TVisualObject::TOS_SELECTED) {
      int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
      pTab->SetActivePane(nIndex);
    }
  }
}

// Get owner pointer (CFrameWnd derived class)
CWnd *TVisualFramework::GetWnd(void)
{
  return m_pOwner;
}

// Get safe owner pointer (CFrameWnd derived class)
CWnd *TVisualFramework::GetSafeWnd(void)
{
  if (m_pOwner && ::IsWindow(m_pOwner->m_hWnd))
    return m_pOwner;
  return NULL;
}

// Get window associated with the visual object specified with its id
// Can be any object (view, splitter or tab window)
CWnd *TVisualFramework::GetObject(DWORD dwId)
{
  TVisualObject *pObject = findObject(dwId);
  if (pObject == NULL)
    return NULL;
  return pObject->m_pWnd;
}

// Get ID associated with the visual object specified with its window pointer
// Can be any object (view, splitter or tab window)
DWORD TVisualFramework::GetObject(CWnd *pWnd)
{
  ASSERT(pWnd);
  TVisualObject *pObject = findObject(pWnd);
  if (pObject == NULL)
    return NULL;
  return pObject->m_dwId;
}

// Return a visual object with the specified id
TVisualObject *TVisualFramework::Get(DWORD dwId)
{
  return findObject(dwId);
}

// Return a visual object with the specified window
TVisualObject *TVisualFramework::Get(CWnd *pWnd)
{
  return findObject(pWnd);
}

// Returns an object that represents the currently active tab within the
// supplied tab window object. This may not be the active pane
TVisualObject *TVisualFramework::GetActiveTab(TVisualObject *pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  if (!pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
    return NULL;

  TTabWnd *pTab = (TTabWnd*)pObject->m_pWnd;
  int nIndex = pTab->GetTabIndex();
  CWnd *pWnd = pTab->GetTabWnd(nIndex);
  ASSERT(pWnd);
  
  return Get(pWnd);
}

// Set the active tab of the parent tab window. This will not activate the
// pane associated with the active tab.
BOOL TVisualFramework::SetActiveTab(TVisualObject *pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (!pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd)))
    return FALSE;

  TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  return pTab->SetActivePane(nIndex,FALSE);
}

// Returns TRUE if object is a tab within a tab window
BOOL TVisualFramework::IsTabPane(TVisualObject* pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd)))
    return TRUE;
  return FALSE;
}

// Returns TRUE if object is a tab window
BOOL TVisualFramework::IsTabWindow(TVisualObject* pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
    return TRUE;
  return FALSE;
}

// Returns TRUE if object is a pane within a splitter window
BOOL TVisualFramework::IsSplitterPane(TVisualObject* pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
    return TRUE;
  return FALSE;
}

// Returns TRUE if object is a pane within a splitter window
BOOL TVisualFramework::IsSplitterWindow(TVisualObject* pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
    return TRUE;
  return FALSE;
}

// Returns TRUE if object is derived from CView
BOOL TVisualFramework::IsView(TVisualObject *pObject)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CView)))
    return TRUE;
  return FALSE;
}

// Get the count of visual objects
int TVisualFramework::GetCount(void)
{
  return m_ObjectMap.size();
}

// Set font for complete framework
void TVisualFramework::SetFont(CFont *pFont)
{
  ASSERT(pFont);
  
  TVisualObject *pObject;
  TVisualObjectMap::iterator mapit;
  for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit ++) {
    pObject = mapit->second;
    if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
      ((TTabWnd*)pObject->m_pWnd)->SetFont(pFont);
    } else if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TVisualFormView))) {
      ((TVisualFormView*)pObject->m_pWnd)->SetFont(pFont);
    } else {
      pObject->m_pWnd->SetFont(pFont);
    }
  }
}

// Enable/disable CtrlTab for tab window
void TVisualFramework::EnableCtrlTab(BOOL bEnable)
{
  // If framework is used in an MDI application, then Ctrl+Tab is used to switch
  // among open windows. If it is enabled, Ctrl+Tab will then switch among 
  // tab panes within the MDI child frame window (this disables default Ctrl+Tab
  // for MDI windows).
  // Ctrl+Tab works only if CWinApp derived class overloads PreTranslateMessage
  // and calles ProcessMessage() of the active TVisualFramework object
  m_bEnableCtrlTab = bEnable;
}

// Since CSplitterWnd does not support dynamic creation, this is a chance for
// derived class to supply CSplitterWnd derived class instead of CSplitterWnd
CSplitterWnd *TVisualFramework::CreateSplitter(DWORD dwId)
{
  return new CSplitterWnd;
}

// Set focus to visual object 
BOOL TVisualFramework::SetActivePane(TVisualObject *pObject)
{
  ASSERT(pObject);

  // Cannot set focus to splitter or tab window
  if (!pObject->CanFocus())
    return FALSE;

  // Cannot set focus to disabled window
  BOOL bEnabled;
  if (pObject->IsEnabled(bEnabled) && !bEnabled)
    return FALSE;

  // Build a list that walks thru the object hierarchy from specified 
  // object to the root
  TVisualObjectList list;
  TVisualObject *pObj = pObject;
  while (pObj) {
    list.insert(list.end(),pObj);
    pObj = pObj->m_pOwner;
  }
  
  // Reverse the list so that we can walk from root to the desired object
  list.reverse();
  
  // Now, walk thru the list and set focus as desired
  TVisualObjectList::iterator it;
  for (it = list.begin(); it != list.end(); it ++) {
    pObj = *it;
    if (pObj->m_pOwner && pObj->m_pOwner->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
      TTabWnd *pTab = (TTabWnd*)pObj->m_pOwner->m_pWnd;
      int nIndex = pTab->GetTabIndex(pObj->m_pWnd);
      if (!pTab->SetActivePane(nIndex)) 
        return FALSE;
    }
  }

  // Update framework owner
  CFrameWnd *pFrame = (CFrameWnd*)m_pOwner;
  pFrame->SetActiveView((CView*)pObject->m_pWnd);

  return TRUE;
}

// Return a pointer to visual object that represents the currently active pane
TVisualObject *TVisualFramework::GetActivePane(void)
{
  CFrameWnd *pFrame = (CFrameWnd*)m_pOwner;
  ASSERT(pFrame);
  CView *pView = pFrame->GetActiveView();
  ASSERT(pView);
  return findObject(pView);
}

// Enable/disable a view. Returns TRUE if sucessful
BOOL TVisualFramework::Enable(TVisualObject *pObject, BOOL bEnable)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CView))) {
    pObject->m_bEnabled = bEnable;
    pObject->m_pWnd->EnableWindow(bEnable);
    if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
      TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
      int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
      pTab->Enable(nIndex, bEnable);
    }
    return TRUE;
  }
  return FALSE;
}

// Enable/disable a tab
BOOL TVisualFramework::EnableTab(TVisualObject *pObject, BOOL bEnable)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  // Check if parent is a tab window
  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
    int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
    if (nIndex == pTab->GetTabIndex())
      return FALSE;
    pTab->EnableTab(nIndex, bEnable);
    pObject->m_bEnabled = bEnable;
    return TRUE;
  }
  return FALSE;
}

// Show/hide a tab
BOOL TVisualFramework::ShowTab(TVisualObject *pObject, BOOL bShow)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (!pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd)))
    return FALSE;

  TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
  int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
  if (nIndex == pTab->GetTabIndex())
    return FALSE;

  pTab->ShowTab(nIndex, bShow);
  return TRUE;
}

// Is object enabled. Returns FALSE if this is not a valid call for the supplied
// object. If return code is TRUE, check bEnabled
BOOL TVisualFramework::IsEnabled(TVisualObject *pObject, BOOL& bEnabled)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);

  bEnabled = pObject->m_bEnabled;
  return TRUE;
  /*
  if (pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(CView))) {
    bEnabled = pObject->m_pWnd->IsWindowEnabled();
    return TRUE;
  }
  return FALSE;
  */
}

// Is tab enabled. Returns FALSE if this is not a valid call for the supplied
// object. If return code is TRUE, check bEnabled
BOOL TVisualFramework::IsTabEnabled(TVisualObject *pObject, BOOL& bEnabled)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
    int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
    bEnabled = pTab->IsTabEnabled(nIndex);
    return TRUE;
  }
  return FALSE;
}

// Is tab visible. Returns FALSE if this is not a valid call for the supplied
// object. If return code is TRUE then check bVisible.
BOOL TVisualFramework::IsTabVisible(TVisualObject *pObject, BOOL& bVisible)
{
  ASSERT(pObject);
  ASSERT(pObject->m_pWnd);
  ASSERT(pObject->m_pParent);

  if (pObject->m_pParent->IsKindOf(RUNTIME_CLASS(TTabWnd))) {
    TTabWnd *pTab = (TTabWnd*)pObject->m_pParent;
    int nIndex = pTab->GetTabIndex(pObject->m_pWnd);
    bVisible = pTab->IsTabVisible(nIndex);
    return TRUE;
  }
  return FALSE;
}

// This should be called from CWinApp derived PreTranslateMessage to handle
// any framework related messages
BOOL TVisualFramework::ProcessMessage(MSG *pMsg)
{
  ASSERT(pMsg);
  if (pMsg->message == WM_KEYDOWN) {
    // Handle Ctrl+Tab for tab windows
    if (m_bEnableCtrlTab) {
      if ((pMsg->wParam == VK_TAB) && (::GetAsyncKeyState(VK_CONTROL) != 0)) {
        CWnd *pWnd = CWnd::FromHandle(pMsg->hwnd);
        ASSERT(pWnd);
        if (pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
          return FALSE;
        // If we are in form view then pWnd is a control
        while (pWnd && !pWnd->IsKindOf(RUNTIME_CLASS(CView)))
          pWnd = pWnd->GetParent();
        // Find object for this window
        ASSERT(pWnd);
        TVisualObject *pObject = findObject(pWnd);
        ASSERT(pObject != NULL);
        while (pObject && !pObject->m_pWnd->IsKindOf(RUNTIME_CLASS(TTabWnd)))
          pObject = pObject->m_pOwner;
        if (pObject) {
          TTabWnd *pTab = (TTabWnd*)pObject->m_pWnd;
          BOOL bShift = (::GetAsyncKeyState(VK_SHIFT) != 0);
          int nIndex = pTab->GetTabIndex();
          int nNdx = nIndex;
          // Switch to new pane (skip invisible and disabled)
          do {
            if (bShift) {
              // Does not work
              nNdx--;
              if (nNdx < 0)
                nNdx = pTab->GetTabCount()-1;
            } else {
              nNdx ++;
              if (nNdx == pTab->GetTabCount())
                nNdx = 0;
            }
          } while (!pTab->SetActivePane(nNdx) && (nNdx != nIndex));
          return TRUE;
        }
      }
    }
  } else if (pMsg->message == WM_SYSKEYDOWN) {
    // Handle hot keys for views (if defined)
    TVisualObject *pObject;
    TVisualObjectMap::iterator mapit;
    for (mapit = m_ObjectMap.begin(); mapit != m_ObjectMap.end(); mapit ++) {
      pObject = mapit->second;
      if (pObject->m_cHotKey == pMsg->wParam) {
        if (SetActivePane(pObject))
          return TRUE;
      }
    }
  }

  return FALSE;
}

BOOL TVisualFramework::OnCmdMsg(UINT nID, int nCode, void* pExtra, 
                             AFX_CMDHANDLERINFO* pHandlerInfo) 
{
	// TODO: Add your specialized code here and/or call the base class
	
	return CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

//=============================================================================
// class TVisualFormView
//
// This class implements the code to set the font for all child controls.
// If it belongs to the visual framework (as a pane) then setting the font
// for the framework's panes will automatically set the font for all child
// controls in the form view.
//=============================================================================

static BOOL __stdcall setChildFont(HWND hwnd, LPARAM lparam);
static BOOL __stdcall setChildEnabled(HWND hwnd, LPARAM lparam);

IMPLEMENT_DYNAMIC(TVisualFormView, CFormView)

BEGIN_MESSAGE_MAP(TVisualFormView, CFormView)
	//{{AFX_MSG_MAP(TVisualFormView)
	ON_WM_ENABLE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

TVisualFormView::TVisualFormView(LPCTSTR lpszTemplateName)
	: CFormView(lpszTemplateName)
{
}

TVisualFormView::TVisualFormView(UINT nIDTemplate)
	: CFormView(nIDTemplate)
{
}

void TVisualFormView::SetFont(CFont *pFont)
{
  ASSERT(pFont);
  ::EnumChildWindows(m_hWnd, ::setChildFont, (LPARAM)pFont);
}

void TVisualFormView::OnEnable(BOOL bEnable) 
{
	CFormView::OnEnable(bEnable);
  ::EnumChildWindows(m_hWnd, ::setChildEnabled, (LPARAM)bEnable);
}

// lParam is a pointer to CFont object
BOOL __stdcall setChildFont(HWND hwnd, LPARAM lparam)
{
  CFont *pFont = (CFont*)lparam;
  ASSERT(pFont);
  CWnd *pWnd = CWnd::FromHandle(hwnd);
  ASSERT(pWnd);
  pWnd->SetFont(pFont);
  return TRUE;
}

// lParam is a BOOL
BOOL __stdcall setChildEnabled(HWND hwnd, LPARAM lparam)
{
  BOOL bEnabled = (BOOL)lparam;
  CWnd *pWnd = CWnd::FromHandle(hwnd);
  ASSERT(pWnd);
  pWnd->EnableWindow(bEnabled);
  return TRUE;
}

/*#############################################################################
# End of file VISUALFX.CPP
#############################################################################*/